var ObjectID = require("mongodb").BSONPure.ObjectID;

PostProvider = function(db) {
  var base = this;
  db.collection("posts", function (err, posts) {
    db.collection("top_tags", function (err, topTags) {
      base.findAll = function(callback) {
        posts.find().toArray(function(err, data) {
          data.sort(function(a, b) {
            return (b.likes.length - b.dislikes.length) -
                   (a.likes.length - a.dislikes.length);
          });
          callback(err, data);
        });
      }

      base.findById = function(id,callback) {
        posts.findOne({_id:new ObjectID(id)}, function(err, data) {
          callback(err, data);
        });
      }

      base.findByTag = function(tag,callback) {
        posts.find({"tags.tag":tag}).toArray(function(err, data) {
          callback(err, data);
        });
      }

      base.findTopic = function(id,callback) {
        base.findById(id, function(err, data) {
          var topic_id;
          if (data.reply_to) topic_id = data.reply_to;
          else topic_id = new ObjectID(id);
          posts.find({$or:[{_id:topic_id},{reply_to:topic_id}]}).toArray(function (err, data) {
            callback(err, data);
          });
        });
      }

      base.allTopics = function(callback) {
        posts.find({reply_to:null}, function(err, data) {
          callback(err, data);
        });
      }

      base.insert = function(author, title, body, reply_to, tags, callback) {
        doc = { author:author, title:title, body:body, created:new Date(),
                editor:null, edited:null, reply_to:new ObjectID(reply_to),
                likes:[], dislikes:[], favs:[], tags:[]};
        for (var i in tags) {
          doc.tags.push({tag:tags[i], user:author, date:new Date()});
        }
        posts.insert(doc, {safe:true}, function(err, data) {
          callback(err, data);
        });
      }

      base.like = function(user_id,post_id,callback) {
        var action = {$push:{likes:{user:user_id, date:new Date()}}};
        rate(user_id,post_id,action,callback);
      }

      base.dislike = function(user_id,post_id,callback) {
        var action = {$push:{dislikes:{user:user_id, date:new Date()}}};
        rate(user_id,post_id,action,callback);
      } 

      base.topTags = function(callback) {
        var m = function() {
          for (var i in this.tags) {
            emit(this.tags[i].tag,1);
          }
        }
        var r = function(key,val) {
          var result = 0;
          for (var i in val) {
            result += val[i];
          }
          return result;
        }
        db.command({mapreduce:"posts", map:m.toString(), reduce:r.toString(), out:"top_tags"}, function(err, data) {
          topTags.find({}).sort({value:-1}).toArray(function (err, data) {
            callback(err, data);
          });
        });
      }

      function rate(user_id,post_id,action,callback) {
        post_id = new ObjectID(post_id);
        posts.findOne({_id:post_id, "likes.user":user_id}, function(err, liked) {
          if (liked == null) {
            posts.findOne({_id:post_id, "dislikes.user":user_id}, function(err, disliked) {
              if (disliked == null) {
                posts.update({_id:post_id}, action, function(err, updated) {
                  callback(err, updated);
                });
              }
              else {
                posts.update({_id:post_id}, {$pull:{dislikes:{user:user_id}}}, function(err, updated) {
                  callback(err, updated);
                });
              }
            });
          }
          else {
            posts.update({_id:post_id}, {$pull:{likes:{user:user_id}}}, function(err, updated) {
              callback(err, updated);
            });
          }
        });
      }
    });
  });
}

exports.PostProvider = PostProvider;
